package net.oschina.cloudecho.util;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;

/**
 * Class扫描工具类
 * 
 * @author yong.ma
 */
public class ClassScanner {
	private static final ResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver();
	private static final MetadataReaderFactory READER_FACTORY = new SimpleMetadataReaderFactory();

	private String basePackage;
	private String classpath;

	/**
	 * 构造函数
	 * 
	 * @param basePackage e.g. net.oschina.cloudecho
	 * @param classpath 查找路径 e.g.
	 *            "classpath*:net/oschina/cloudecho/&lowast;&lowast;/*.class"
	 */
	public ClassScanner(String basePackage, String classpath) {
		this.basePackage = basePackage;
		this.classpath = classpath;
	}

	@SuppressWarnings("rawtypes")
	public List<Class> lookup(boolean onlyConcrete) {
		List<Class> result = new ArrayList<Class>();

		Class<?> guessType = null;
		try {
			guessType = Class.forName(basePackage);
		} catch (ClassNotFoundException e1) {
			// NO-OP
		}

		Resource[] resources;
		try {
			resources = RESOLVER.getResources(this.classpath);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		for (Resource res : resources) {
			try {
				MetadataReader meta = READER_FACTORY.getMetadataReader(res);
				String className = meta.getClassMetadata().getClassName();
				Class<?> clazz = Class.forName(className);
				boolean b = !onlyConcrete || isConcrete(clazz);
				b &= (guessType == null ? className.startsWith(basePackage) : guessType.isAssignableFrom(clazz));
				if (b) {
					result.add(clazz);
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		return result;
	}

	boolean isAbstract(Class<?> clazz) {
		return clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
	}

	boolean isConcrete(Class<?> clazz) {
		return !isAbstract(clazz);
	}
}
